package com.liyong.pullorder.sdk.retry;

import com.liyong.pullorder.sdk.autoconfigure.PullOrderSdkProperties;
import com.liyong.pullorder.sdk.exception.AuthException;
import com.liyong.pullorder.sdk.exception.DataParseException;
import com.liyong.pullorder.sdk.exception.NetworkException;
import com.liyong.pullorder.sdk.exception.RateLimitException;
import com.liyong.pullorder.sdk.util.AlertManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResourceAccessException;

import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 葡萄味的萝卜
 * @date 2025/10/21 12:14
 * 重试机制配置类
 * 核心作用：集成Spring Retry实现智能重试策略，应对网络不稳定场景
 * 设计目标：通过指数退避机制避免雪崩效应，提高系统韧性
 *
 * 重试策略：
 * - 指数退避：重试间隔逐渐延长
 * - 最大重试次数：防止无限重试
 * - 异常分类：仅对可重试异常进行重试
 * - 上下文传递：保持重试间的状态一致性
 */
@Slf4j
@Configuration
@EnableRetry
public class RetryConfig {
    private final PullOrderSdkProperties properties;

    public RetryConfig(PullOrderSdkProperties properties) {
        this.properties = properties;
        log.info("初始化重试配置 - 最大重试次数: {}, 初始间隔: {}ms, 退避倍数: {}",
                properties.getRetry().getMaxAttempts(),
                properties.getRetry().getBackoff(),
                properties.getRetry().getMultiplier());
    }

    /**
     * 创建拉单专用重试模板
     * 应用场景：网络请求、数据解析等可重试操作
     * 特性：针对拉单场景优化，支持异常分类重试
     */
    public RetryTemplate pullOrderRetryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        // 配置重试策略：最大重试次数 + 异常分类
        retryTemplate.setRetryPolicy(retryPolicy());

        // 配置退避策略：指数退避避免集中重试
        retryTemplate.setBackOffPolicy(backOffPolicy());

        // 添加重试监听器，用于监控和日志
        retryTemplate.registerListener(new PullOrderRetryListener());

        return retryTemplate;
    }

    /**
     * 重试策略配置
     * 规则：仅对可重试的异常进行重试，其他异常立即失败
     */
    private SimpleRetryPolicy retryPolicy() {
        Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();

        // 可重试的异常类型
        retryableExceptions.put(NetworkException.class, true);
        retryableExceptions.put(RateLimitException.class, true);
        retryableExceptions.put(SocketTimeoutException.class, true);
        retryableExceptions.put(ConnectException.class, true);
        retryableExceptions.put(ResourceAccessException.class, true);

        // 不可重试的异常类型
        retryableExceptions.put(AuthException.class, false);
        retryableExceptions.put(DataParseException.class, false);
        retryableExceptions.put(IllegalArgumentException.class, false);

        return new SimpleRetryPolicy(
                properties.getRetry().getMaxAttempts(),
                retryableExceptions,
                true, // 遍历异常原因
                false // 不重试所有异常
        );
    }

    /**
     * 指数退避策略配置
     * 公式：interval = initial * (multiplier ^ (retryCount - 1))
     * 示例：初始1秒，倍数2，重试间隔：1s, 2s, 4s, 8s...
     */
    private ExponentialBackOffPolicy backOffPolicy() {
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(properties.getRetry().getBackoff());
        backOffPolicy.setMultiplier(properties.getRetry().getMultiplier());
        backOffPolicy.setMaxInterval(10000L); // 最大间隔10秒
        return backOffPolicy;
    }

    /**
     * 拉单重试监听器
     * 作用：记录重试日志、收集监控指标、触发告警
     */
    private static class PullOrderRetryListener implements RetryListener {

        @Override
        public <T, E extends Throwable> void onError(RetryContext context,
                                                     RetryCallback<T, E> callback,
                                                     Throwable throwable) {
            int retryCount = context.getRetryCount();
            String platform = (String) context.getAttribute("platform");

            log.warn("拉单重试失败 - 平台: {}, 重试次数: {}, 错误: {}",
                    platform, retryCount, throwable.getMessage());

            // 记录监控指标
            MetricsCollector.recordRetryFailure(platform, retryCount, throwable.getClass().getSimpleName());

            // 达到最大重试次数时触发告警
            if (retryCount >= ((SimpleRetryPolicy)context).getMaxAttempts()) {
                log.error("拉单达到最大重试次数 - 平台: {}, 最终错误: {}", platform, throwable.getMessage());
                AlertManager.sendAlert("PULL_ORDER_MAX_RETRY",
                        String.format("平台%s拉单达到最大重试次数，最终错误: %s", platform, throwable.getMessage()));
            }
        }

        @Override
        public <T, E extends Throwable> void close(org.springframework.retry.RetryContext context,
                                                   org.springframework.retry.RetryCallback<T, E> callback,
                                                   Throwable throwable) {
            // 重试结束回调，用于资源清理
        }

        @Override
        public <T, E extends Throwable> boolean open(org.springframework.retry.RetryContext context,
                                                     org.springframework.retry.RetryCallback<T, E> callback) {
            // 重试开始回调，用于初始化上下文
            return true;
        }
    }

    /**
     * 监控指标收集器
     */
    private static class MetricsCollector {
        static void recordRetryFailure(String platform, int retryCount, String errorType) {
            log.debug("记录重试失败指标 - 平台: {}, 重试次数: {}, 错误类型: {}", platform, retryCount, errorType);
        }
    }

//    /**
//     * 告警管理器
//     */
//    private static class AlertManager {
//        static void sendAlert(String alertType, String message) {
//            log.error("发送告警 - 类型: {}, 内容: {}", alertType, message);
//            // 实际对接公司告警平台
//        }
//    }
}
